home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Games
/
Hugo
/
Library
/
objlib.h
< prev
next >
Wrap
Text File
|
1997-05-02
|
45KB
|
1,947 lines
!----------------------------------------------------------------------------
!
! HUGO Object Library v2.3.2 by Kent Tessman (c) 1995-1997
! for use with Hugo Compiler v2.3
!
!----------------------------------------------------------------------------
!
! The Hugo Object Library contains the following useful class
! definitions:
!
! room
! character, female_character
! door
! direction (including direction objects)
! scenery
! component
!
! vehicle
! plural_class, identical_class
! attachable
!
! (NOTE: In order to use the vehicle class, plural_class or
! identical_class, or the attachable class, the flags USE_VEHICLES,
! USE_PLURAL_OBJECTS, or USE_ATTACHABLES, respectively, must be set.)
!
!----------------------------------------------------------------------------
#version 2.3
#ifclear _COMPILING_HUGOLIB
#message warning "OBJLIB.H not included separately from HUGOLIB.H"
#endif
#ifset _COMPILING_HUGOLIB
! BASIC ROOM:
class room "room_class"
{
type room
is static, light, open
}
! STANDARD CHARACTERS:
class character "character_class"
{
type character
pronouns "he", "him", "his", "himself"
capacity 50
holding 0
is living, transparent, static
}
character female_character "female_character_class"
{
type female_character
pronouns "she", "her", "her", "herself"
is female
}
! DOORS:
!
! This door object assumes that the object will ultimately have
! exactly two components to the between property: one on either
! side of the door.
property between alias found_in
class door "door"
{
type door
door_to ! depends on actor location
{
local currentroom
if actor = player
currentroom = location
else
currentroom = parent(actor)
! sitting on or in something
#ifclear USE_VEHICLES
if actor = player and player not in location
#endif
#ifset USE_VEHICLES
if actor = player and player not in location and
parent(player).type ~= vehicle:
#endif
{
! "You'll have to get out first..."
OMessage(door, 1)
return true
}
if self is not open and actor = player
{
#ifclear NO_VERBS
if verbroutine = &DoGo and self is not locked and
self is openable:
{
OMessage(door, 2) ! "(opening it first)"
Perform(&DoOpen, self)
main ! counts as a turn
if self is not open
return false
}
elseif verbroutine = &DoGo and self is lockable
{OMessage(door, 3) ! "It is locked."
return true}
else
{
#endif
OMessage(door, 4) ! "It is closed."
return true
#ifclear NO_VERBS
}
#endif
}
if self is not open and (actor in location or
location = self.between #((parent(actor) = \
self.between #1) + 1))
{
! Notify the player that the player or a character
! has gone through the door:
!
OMessage(door, 5)
general = 2
}
if currentroom = self.between #1
return self.between #2
else
return self.between #1
}
long_desc
{
if self is open
OMessage(door, 6) ! when open
else
OMessage(door, 7) ! when closed
}
is openable, static
}
! COMPASS DIRECTIONS:
property dir_to alias capacity ! for directions only
property dir_from alias size !
class direction "direction_class"
{
type direction
article "the"
found_in
{
if self in direction
return location
}
is static, known
}
direction n_obj "north"
{in direction
nouns "n", "north"
dir_to n_to
dir_from s_obj}
direction ne_obj "northeast"
{in direction
nouns "ne", "northeast"
dir_to ne_to
dir_from sw_obj}
direction e_obj "east"
{in direction
nouns "e", "east"
dir_to e_to
dir_from w_obj}
direction se_obj "southeast"
{in direction
nouns "se", "southeast"
dir_to se_to
dir_from nw_obj}
direction s_obj "south"
{in direction
nouns "s", "south"
dir_to s_to
dir_from n_obj}
direction sw_obj "southwest"
{in direction
nouns "sw", "southwest"
dir_to sw_to
dir_from ne_obj}
direction w_obj "west"
{in direction
nouns "w", "west"
dir_to w_to
dir_from e_obj}
direction nw_obj "northwest"
{in direction
nouns "nw", "northwest"
dir_to nw_to
dir_from se_obj}
direction u_obj "above"
{in direction
nouns "u", "up"
article ""
dir_to u_to
dir_from d_obj}
direction d_obj "below"
{in direction
nouns "d", "down"
article ""
dir_to d_to
dir_from u_obj}
direction in_obj "in"
{in direction
noun "in", "inside"
dir_to in_to
dir_from out_obj}
direction out_obj "out"
{in direction
noun "out", "outside"
dir_to out_to
dir_from in_obj}
! SCENERY:
!
! Scenery objects are accessible to actions, but are not individually listed
! as part of the room contents (although their children are). They are
! intended to be mentioned in the room description, and still require
! explicit names, articles, nouns, and adjectives. The type property
! of a scenery object should never be changed.
class scenery "scenery_class"
{
type scenery
exclude_from_all
{
if verbroutine = &DoGet
return true
else
return false
}
is static, hidden
}
! COMPONENT:
!
! Components are essentially movable scenery objects, which are considered
! to be attached to another object. The part_of property is initialized
! with the object of which the new object is a component. Note that
! children of components are not listed in the room description; their
! children are, however, listed when directly examining the object.
property part_of alias found_in
scenery component "component_class"
{
type component
part_of nothing
before
{
object DoGet
{
OMessage(component) ! "You can't separate that..."
}
}
}
! VEHICLE:
!
! This vehicle object expects the object's vehicle_move property routine
! to return true if the vehicle is presently capable of movement; if it
! isn't, vehicle_move is responsible for printing an appropriate message
! before returning false.
!
! The vehicle_verbs property contains a list of the valid verbs for vehicle
! movement, while the vehicle_path property contains a list of all the
! vehicles which may travel in a particular room object.
!
! The DoMoveinVehicle routine may conceivably be used by all basic vehicles
! in the game. However, new grammar incorporating the vehicle movement verbs
! for each object (corresponding exactly to the list under vehicle_verbs)
! must be specified, as in:
!
! verb "<verb1>"[, "<verb2>",...]
! * DoMoveinVehicle
! * object DoMoveinVehicle
! ...
!
! For different vehicle objects, the verbs (and syntax) may change, but
! the call to DoMoveinVehicle remains the same.
!
! Because of the relative complexity of the vehicle object, it must be
! explicitly included by setting the USE_VEHICLES flag.
#ifset USE_VEHICLES
property vehicle_move ! for vehicles only
property vehicle_verbs ! " " "
property vehicle_verb alias vehicle_verbs
property vehicle_path ! for rooms only
class vehicle "vehicle_class"
{
type vehicle
vehicle_verb "drive" ! default verb
prep "in", "out" ! " prepositions
vehicle_move true ! by default, always ready to move
before
{
parent(player) DoGo
{
! "To walk, you'll have to get out..."
OMessage(vehicle, 1, self)
}
}
is enterable, static
}
routine DoMoveinVehicle
{
local v, moveto
if player in location
{
OMessage(vehicle, 2) ! "You aren't in anything."
return false
}
v = parent(player) ! the vehicle
if v.before: return true
! Match the verb
if not InList(v, vehicle_verbs, word[1])
{OMessage(vehicle, 3, v) ! wrong verb for this vehicle
return false}
if not object or (object = v and player in object)
{OMessage(vehicle, 4) ! "Specify a direction as well..."
return false}
if not v.vehicle_move: return true
if obstacle
{
OMessage(vehicle, 5, v) ! "X stops you from moving."
return true
}
if object.type ~= direction
{
moveto = object.door_to
if not moveto
! "You can't (drive) in there..."
OMessage(vehicle, 6, v)
if moveto <= 1
return
}
else
moveto = location.(object.dir_to)
if not InList(moveto, vehicle_path, v) and moveto ~= 1
{
OMessage(vehicle, 7, v) ! "You can't (drive) that way."
return false
}
elseif moveto = 0
{
if not location.cant_go
OMessage(vehicle, 7, v)
return false
}
elseif moveto = 1 ! already printed message
return false ! (moveto is never 1)
#ifset USE_ATTACHABLES
if ObjectisAttached(v, location, moveto)
return false
#endif
! Finally, the vehicle can move
move v to moveto
v is moved
old_location = location
location = moveto
#ifset USE_ATTACHABLES
MoveAllAttachables(v, old_location, location)
#endif
if not FindLight(location)
DarkWarning
else
{DescribePlace(location)
location is visited}
run parent(player).after
return true
}
#endif ! ifset USE_VEHICLES
! PLURAL OBJECTS:
!
! (Based on an implementation proposed by Jim Newland.)
!
! Plural objects are unique objects that can be referred to by a common
! group name, such as "books" for a dictionary and a cookbook. Identical
! objects are similar, except that the case is one of, for example, "coins"
! for two identical coins.
!
! The plural_of property contains a list of all objects belonging to the
! given plural_class (or identical_class). The property single_noun holds
! the singular form(s) of the object name, i.e. "book" or "coin".
!
! Member objects are defined simply as objects, without using the class
! name in the declaration. For identical objects, the identical_to
! property must be defined, giving the name of the identical_class to
! which the object belongs. As well, the object in any action performed
! on an identical object is generally assumed to be the identical_class.
! That is, properties such as long_desc should be defined for the
! identical_class.
!
! Words in the adjectives property of the member objects should also be
! adjectives for the given plural or identical class.
!
! (NOTE: Members of a plural_class may have plural_is (essentially
! identical_to for plural objects), but it is optional and used only for
! object listing.)
!
! The plural_verbs property should return true if the current verbroutine
! is in the list of valid actions for the class in question.
!
! The Init routine must contain the line: InitPluralObjects
#ifset USE_PLURAL_OBJECTS
global plural_count ! number of plural/identical classes used
constant MAX_PLURALS 32 ! maximum number of classes
array plurals[MAX_PLURALS]
property plural_of ! contains a list of all individual objects
property single_noun ! i.e. "thing" vs. "things"
property identical_to ! for member objs., set to the
! identical_class to which they belong
property plural_is alias identical_to
property plural_verbs ! returns true for valid verbroutines
! Each plural or identical object must have a property giving the total
! number specified:
property pluralobj_number alias nw_to
global pluralobj_heldmode ! 1, -1, 0 for held, notheld, or neither
class plural_class "plural_class"
{
type plural_class
in_scope 0 ! may be an actor at some point
size 0 ! must be weightless
pluralobj_number 0
plural_verbs
{
if verbroutine = &DoLook, &DoGet, &DoDrop, &DoPutin
return true
else: return false
}
before
{
object ! intercepts all plural_object verbs
{
local i, j, n, obj, count, flag
! Count the number of workflagged member objects, to see if there are
! less than specified (or any at all, for that matter).
for (i=1; i<=self.#plural_of; i++)
{
if self.plural_of #i is workflag
{
obj = self.plural_of #i
AssignPronoun(obj)
n++
}
}
if n > 1: AssignPronoun(self)
if n = 0
{
! "There are none..."
OMessage(plural_class, 1)
return true
}
if (n < self.pluralobj_number) or
(n = 1 and self.pluralobj_number = 0):
{
! "There are only x things..."
OMessage(plural_class, 2, n)
return true
}
! If it's not an allowable plural-objects action, disqualify it:
if self.pluralobj_number ~=1 and not self.plural_verbs
{
! "You'll have to do that one at a time."
OMessage(plural_class, 3)
return true
}
! Handle examining objects as a group specially:
if verbroutine=&DoLook
{
if self.type = identical_class
return false
if self.long_desc
return true
flag = 0
for (i=1; i<=self.#plural_of; i++)
{
j = self.plural_of #i
if &j.long_desc
flag = true
}
if flag = 0: return false
}
! Finally, process the action on workflagged individual objects:
for (i=1; i<=self.#plural_of; i++)
{
obj = self.plural_of #i
if obj is workflag
{
if n~=1 and self.pluralobj_number~=1
{
if self.type = identical_class and
obj.article:
{print CountWord(count+1);
print " ";}
print obj.name; ": ";
}
Perform(verbroutine, obj, xobject)
count++
AssignPronoun(obj)
}
! number of objects specified (if specified)
if count=self.pluralobj_number and count>0
break
}
}
}
is plural, known
}
plural_class identical_class "identical_class"
{
type identical_class
}
! InitPluralObjects must be called in the Init routine in order for plural
! or identical objects to be used.
routine InitPluralObjects
{
local i, count
for (i=1; i<=objects; i++)
{
if (i.type = plural_class or i.type = identical_class) and
i ~= plural_class and i~=identical_class
{
plurals[count] = i
if ++count >= MAX_PLURALS
{
"[WARNING: Maximum number of plural/identical
classes exceeded. Increase MAX_PLURALS in
the Hugo Object Library.]"
return
}
}
}
plural_count = count
}
! ParsePluralObjects is called by the Parse routine.
routine ParsePluralObjects
{
local i, j, k, n, w, wn
local any, plist, pobj, plural_type, loc, xobj, pobj_number
local 2ndpass
! Before doing anything, move all plural/identical object classes back to
! their respective holding classes:
for (i=0; i<plural_count; i++)
move plurals[i] to plurals[i].type
if actor = 0: actor = player
pluralobj_heldmode = 0 ! i.e. not explicitly held or not
wn = 2 ! starting word number
:ScanforPluralWords
pobj_number = 0 ! default explicit count (pluralobj_number)
! The first step is to scan the phrase for a potential plural object word:
for (w=wn; w<=words; w++)
{
if word[w] = 0 or w>words
break ! end of phrase
if word[w] = "my"
{
pluralobj_heldmode = 1
}
pobj = 0
for plist in plural_class
{
if InList(plist, single_noun, word[w])
{pobj = plist
plural_type = single_noun
break}
if InList(plist, noun, word[w])
{
pobj = plist
plural_type = noun
break
}
}
if pobj: break
for plist in identical_class
{
pobj = 0
if InList(plist, single_noun, word[w])
{
pobj = plist
pobj_number = 1
plural_type = single_noun
break
}
if InList(plist, noun, word[w])
{
pobj = plist
plural_type = noun
break
}
}
if pobj: break
}
if not pobj: return false ! didn't find anything
! At this point, <w> points to the word and <pobj> is the object number of
! the plural class. <plural_type> is either noun or single_noun. What
! follows is the actual parsing of the plural structure:
! Determine (roughly) if object is explicitly held or notheld; the
! VerbHeldmode routine checks to see if the verb necessarily implies a
! specific heldmode by checking the first 6 words--i.e., even if the first
! five represent an object name in an actor-directed command.
if not 2ndpass
{
for (i=1; i<=6 and i<=w; i++)
{
if pluralobj_heldmode: break
if word[i] = "": break ! end of phrase
VerbHeldmode(word[i])
}
! If it is a single_noun (i.e. "any thing" or "either thing"), the object
! global will have to be appropriately determined. <pobj> will hold the
! selected object (if any). In any case, it will be necessary to know if
! at least one object (in the case of multiple plural object specification)
! is available, in order to move the plural_class into scope.
! To boot, it could also get ugly if a container or platform xobject is
! specified as the location for the object(s) being acted upon. There is
! no polite way of figuring out what the xobject might be at this point.
for (i=w; i<words; i++)
{
k = word[i] ! for shorthand purposes only
if k = 0: break ! end of phrase
if (k = "from", "outof", "offof", "off") or
(pluralobj_heldmode ~= 1 and
(k = "in" or k = "inside" or k = "on")):
{
xobj = 0 ! nothing selected yet
do
{
i=i+1 ! get next word
for (j=1; j<=pobj.#plural_of; j++)
{
n = parent(pobj.plural_of #j)
if ObjWord(word[i], n)
xobj = n
}
}
while i<=words and word[i]~=0
break
}
}
n = 0
if xobj
loc = xobj
elseif actor = player
loc = location
else
loc = parent(actor)
}
! Remove the old plural_class adjective(s), if any
while w>1 and (ObjWord(word[w-1], pobj)=adjective or word[w-1]="my")
{
DeleteWord(w-1)
w--
}
! Then, find out if there is a preceding number
if w > 1
{
k = WordisNumber(word[w-1])
if k >= 1 ! e.g., "one (or more) things"
{
DeleteWord(w-1)
w--
pobj_number = k
}
if WordisNumber(word[w-1])>=1 ! "two of the three"
{
pobj_number = WordisNumber(word[w-1])
DeleteWord(w-1)
w--
}
}
! Remove a preceding "all" or "any"; i.e. "all things" is the same as "things"
if w>1 and (word[w-1] = "~all" or word[w-1] = "~any")
{
if word[w-1] = "~any": any = true
DeleteWord(w-1)
w--
}
! Now see if the object (or a member of the plural_class) is available given
! the restrictions of pluralobj_heldmode:
n = 0
j = 0
if pluralobj_heldmode = -1 ! explicitly notheld object
{
for (i=1; i<=pobj.#plural_of; i++)
{
k = pobj.plural_of #i
k is not workflag
if k in loc
{
n = k
k is workflag
j++
}
if n and plural_type = single_noun: break
}
}
if not n or pobj_number = 0 or j < pobj_number
{
for (i=1; i<=pobj.#plural_of; i++)
{
k = pobj.plural_of #i
! explicitly notheld object
if pluralobj_heldmode = -1 and k is not workflag
{
if FindObject(k, loc) and k not in actor
{
n = k
k is workflag
j++
}
}
elseif pluralobj_heldmode ~= -1
k is not workflag
! explicitly held object
if pluralobj_heldmode = 1
{
if k in actor
{
n = k
k is workflag
j++
}
}
! or neither
elseif pluralobj_heldmode = 0
{
if FindObject(k, loc)
{
n = k
k is workflag
j++
}
}
if n and plural_type = single_noun: break
if j and j = pobj_number: break
}
}
! Even if no object is found, it will be necessary to insert one in the
! input line to generate a "You don't see that"-esque parser message in
! the case of a single_noun specification.
if plural_type = single_noun and not n
n = pobj.plural_of #1
! If it's not a plural specification--i.e. it is an "any" or "either"
! phrasing instead--change the plural object word to represent <n>.
i = words
if plural_type = single_noun and pobj.type ~= identical_class and any
{
SetObjWord(w, n)
if pobj.type = plural_class
print "("; The(n); ")"
}
! If it is a plural specification--plural_class.noun instead of single_noun--
! move the plural class into an accessible position, but only if one of
! the component members is available. The same goes for identical_classes.
elseif n and (plural_type = noun or pobj.type = identical_class)
{
! Insert the name of the identical_class, if necessary
if pobj.type = identical_class
SetObjWord(w, pobj)
if pluralobj_heldmode = 1 ! in inventory
move pobj to actor
elseif xobj ! in or on something
move pobj to xobj
else ! anything else
PutinScope(pobj, actor)
}
pobj.pluralobj_number = pobj_number ! # of plural objects
wn = w + pobj.#adjectives + (pobj.noun > 0)
if wn < i
{
pobj = 0
2ndpass = true
jump ScanforPluralWords
}
return true
}
! VerbHeldmode sets the pluralobj_heldmode global to the appropriate value
! 1, -1, or 0, based on the verb being used; returns true if the verb
! indicates a particular heldmode. This routine can be replaced in order
! to change the selection of verbs.
routine VerbHeldmode(a)
{
if a = "get", "take", "pick", "remove":
pluralobj_heldmode = -1 ! explicitly notheld
elseif a = "drop", "put", "leave", "place", "throw",
"give", "offer":
pluralobj_heldmode = 1 ! explicitly held
return (pluralobj_heldmode~=0)
}
! WordisNumber returns the number given by the specified word (where the
! argument is, for example, word[x]). If the word is not a number, or
! is out of range, -1 is returned.
routine WordisNumber(w)
{
select w
case "zero", "0": return 0
case "one", "1": return 1
case "two", "2": return 2
case "three", "3": return 3
case "four", "4": return 4
case "five", "5": return 5
case "six", "6": return 6
case "seven", "7": return 7
case "eight", "8": return 8
case "nine", "9": return 9
case "ten", "10": return 10
case else: return -1
}
! CountWord prints an appropriate article as specified by the value <n>.
routine CountWord(n)
{
select n
case 1: return "first"
case 2: return "second"
case 3: return "third"
case 4: return "fourth"
case 5: return "fifth"
case 6: return "sixth"
case 7: return "seventh"
case 8: return "eighth"
case 9: return "ninth"
case 10: return "tenth"
case else: return "next"
}
! RemoveIdentical removes the object <obj> from its current identical_class;
! used for instances where, for example, an indistinguishable object is
! marked or otherwise made unique.
routine RemoveIdentical(obj)
{
local j, o
for (o=0; o<plurals; o++)
{
j = InList(plurals[o], plural_of, obj)
if j ! if it matches
{
plurals[o].plural_of #j = 0 ! remove it
return true
}
}
return false
}
! AddIdentical adds (i.e. returns) object <obj> to the identical_class <i>.
routine AddIdentical(obj, i)
{
local j
for (j=1; j<i.#plural_of; j++)
{
if i.plural_of #j = 0 ! a blank slot
{
i.plural_of #j = obj ! add it
return j
}
}
return false
}
#endif ! ifset USE_PLURAL_OBJECTS
! ATTACHABLE:
!
! Because ropes, chains, and other such household items are notoriously
! hard to code.
!
! The attachable_to property contains a list of all items to which the
! object may be attached. The attached_to property will ultimately contain
! the objects to which the object is attached; it must be defined with an
! appropriate number of elements. For example, a rope that can be tied
! between two things must have the following in its object definition:
!
! attachable rope "rope" ! or something similar
! {
! ...
! attached_to 0, 0
! ...
! }
!
! so that attached_to can hold a total of two object numbers.
!
! A harness that is already attached to a wagon, but which can also be
! attached to six horses (objects) at the same time, might be initialized
! as follows:
!
! attached_to wagon, 0, 0, 0, 0, 0, 0
!
! with room for seven elements.
!
! The attach_take and attach_drop properties are normally not set. If
! attach_take is true, an attempt to take the attachable is made before
! attaching (or detaching) it. If attach_drop is true, the object is
! automatically dropped after it is attached.
!
! The attach_verbs and detach_verbs properties contain lists of all valid
! verbs to attach or detach the object. Similarly to the vehicle class's
! DoMoveinVehicle routine, the DoAttachObject and DoDetachObject can be
! used by all basic attachables, with new grammar specified for the object
! (corresponding exactly to the lists under attach_verbs and detach_verbs),
! as in:
!
! verb "<verb1>"[, "<verb2>",...]
! * DoVague
! * object DoAttachObject
! * object "to" xobject DoAttachObject
! ...
!
! Note that DoAttachObject expects a second object (the xobject) to be
! given.
!
! To check if a particular object is kept immovable by an attachable,
! call ObjectisAttached(<object>); it returns the number of the attachable,
! or false if there is none.
!
! AttachObject and DetachObject may be used to directly attach or detach
! an attachable object.
!
! Also, any routine that moves the player or player's parent--such as
! MovePlayer or DoMoveinVehicle--should call MoveAllAttachables to
! reconcile the location of attached objects (since they are not necessarily
! connected via the object tree). Objects with the mobile attribute set
! may be dragged.
!
! Non-attachables may have the attach_immobile property, which governs
! whether or not they may be pulled, dragged, etc. by returning 0 when
! freely moveable or 1 (true) if something is keeping it from moving. In
! the second case, attach_immobile is responsible for printing any
! explanatory message.
#ifset USE_ATTACHABLES
property attachable_to
property attached_to
property attach_prep
property detach_prep
property attached_desc
property attach_shortdesc ! Describe attachables using these
property attach_longdesc ! in place of short_desc and long_desc
property attach_verb
property detach_verb
property attach_verbs alias attach_verb
property detach_verbs alias detach_verb
property attach_take ! Normally not set, these are true if an
property attach_drop ! implicit take or drop is required
property attach_immobile ! for non-attachables; returns 0 if moveable,
! 1 if not (after printing explanation)
class attachable "attachable_class"
{
type attachable
attach_verbs "tie", "attach"
detach_verbs "untie", "detach"
attach_prep "to"
detach_prep "from"
attached_desc "tied to"
attached_to 0 ! 1 default attachment point
desc_detail
{
local i
for (i=1; i<=self.#attached_to; i++)
{
if self.attached_to #i
{
print " ("; self.attached_desc; " ";
ListAttachments(self)
print ")";
break
}
}
}
short_desc
{
local a, i
a = 0
! See first if it's attached to anything
for (i=1; i<=self.#attached_to; i++)
{
if self.attached_to #i
a = self.attached_to #i
}
if not a ! not attached to anything
{
a = self.attach_shortdesc
if not a
Message(&DescribePlace, 2, self)
return true
}
else
{
if self.attach_shortdesc
{
print "\_ ";
OMessage(attachable, 3) ! "Attached to..."
}
else: OMessage(attachable, 1)
}
}
long_desc
{
local a, i, desc
a = 0
! See first if it's attached to anything
for (i=1; i<=self.#attached_to; i++)
{
if self.attached_to #i
a = 1
}
if not self.attach_longdesc
OMessage(attachable, 2) ! "You see nothing special..."
if a ! if it's attached to something(s)
{
print ""
OMessage(attachable, 3) ! "Attached to..."
}
}
before
{
object DoGet
{
local i, w
self is not workflag
if self.attach_take or self.attach_drop
{
for (i=1; i<=self.#attached_to; i++)
{
if self.attached_to #i and
self.#attached_to > 1:
! "You'll have to (detach) it..."
{OMessage(attachable, 4)
return true}
elseif self.attached_to #i
! "(having to (detach) it first)"
{OMessage(attachable, 5, i)
w = word[1]
word[1] = self.detach_verb
xobject = self.attached_to #i
if not DoDetachObject
return true
Main ! counts as a turn
word[1] = w
self is workflag
return false}
}
}
return false
}
#ifset VERBSTUBS
object DoTie
{Perform(&DoAttachObject, object, xobject)}
object DoUntie
{Perform(&DoDetachObject, object, xobject)}
#endif
}
after
{
object DoGet
{
if self is not workflag
return false
}
}
}
routine DoAttachObject
{
if object.type ~= attachable
{OMessage(attachable, 6) ! "You can't (attach) that."
return false}
! Match the verb
if not InList(object, attach_verbs, word[1])
{OMessage(attachable, 7, object.attach_verb) ! wrong verb
return false}
if not xobject
{OMessage(attachable, 8) ! "Be more specific..."
return false}
if object.attach_take and object not in player
{
OMessage(attachable, 9) ! "(taking it first)"
Perform(&DoGet, object)
if object not in player
return false
Main ! counts as a turn
}
! See if the attach-to object is appropriate
if not InList(object, attachable_to, xobject)
{
! "You can't (attach) it to that."
OMessage(attachable, 10)
return false
}
! Make sure it's not already attached
if InList(object, attached_to, xobject)
{
! "Except that it is already (attached) to that."
OMessage(attachable, 11)
return false
}
if not ObjectAttach(object, xobject)
{
! "Except that it is already (attached) to..."
OMessage(attachable, 12)
return false
}
if not xobject.after
{
if not object.after
! "You (attach) it to..."
OMessage(attachable, 13)
}
return true
}
routine DoDetachObject
{
local i, j
if object.type ~= attachable
{OMessage(attachable, 6) ! "You can't (detach) that."
return false}
! Match the verb
if not InList(object, detach_verbs, word[1])
{OMessage(attachable, 7, object.detach_verb) ! wrong verb
return false}
! If second object is given, make sure it is attached
if xobject and not InList(object, attached_to, xobject)
{
! "Except that it is not (attached) to that."
OMessage(attachable, 14)
return false
}
elseif not xobject
{
j = 0
for (i=1; i<=object.#attached_to; i++)
{
if object.attached_to #i
{j++
xobject = object.attached_to #i}
}
if j = 0
{
! "Except that it is not (attached) to anything."
OMessage(attachable, 15)
return false
}
elseif j > 1
{
! "Be a little more specific..."
OMessage(attachable, 16)
return false
}
}
if object.attach_take and object not in player and verbroutine~=&DoGet
{
OMessage(attachable, 9) ! "(taking it first)"
Perform(&DoGet, object)
if object not in player
return false
Main ! counts as a turn
}
ObjectDetach(object, xobject)
j = verbroutine
verbroutine = &DoDetachObject
if not xobject.after
{
if not object.after
! "You detach it from..."
OMessage(attachable, 17)
}
verbroutine = j
return true
}
! Use the routines ObjectAttach and ObjectDetach to attach or detach the
! attachable given by <att> to or from the object specified by <obj>; they
! will take care of setting and/or clearing the appropriate properties,
! returning true if successful.
routine ObjectAttach(att, obj)
{
local i, point
if not InList(att, attachable_to, obj): return false
! See first if there is an open point
for (i=1; i<=att.#attached_to; i++)
{
if att.attached_to #i = 0
{point = i
break}
}
if not point: return false ! no open point
att.attached_to #point = obj
if object.attach_drop
move object to location
return point
}
routine ObjectDetach(att, obj)
{
local n
n = InList(att, attached_to, obj)
if n
att.attached_to #n = 0
return true
}
! ObjectisAttached returns true if something in <oldloc> is keeping
! <obj> from moving to <newloc>.
routine ObjectisAttached(obj, oldloc, newloc)
{
local check
! Check first the children of the object in question:
check = Check_AttachableChildren(obj, newloc)
if check: jump SomethingisAttached
! Failing finding any attachments there, use the roundabout method of
! checking the location:
check = Check_Attachedto(obj, oldloc, newloc)
if check: jump SomethingisAttached
return false ! nothing is stopping it
:SomethingisAttached
if check = -1 ! something already printed
return true
#ifset USE_VEHICLES
if verbroutine = &DoGo or verbroutine = &DoMoveinVehicle
#endif
#ifclear USE_VEHICLES
if verbroutine = &DoGo
#endif
{
! "Not while it is still (attached) to..."
OMessage(attachable, 18, check)
}
else
{
! "You can't move whatever is (attached) to it..."
OMessage(attachable, 19, check)
}
return true
}
! Check_Attachable checks all the attachments of <att> for mobility, where
! <att> is an attachable. Returns the number of the immobile object, or
! false if none.
routine Check_Attachable(att, newloc)
{
local a, b, i
for (i=1; i<=att.#attached_to; i++)
{
a = att.attached_to #i
#ifset USE_VEHICLES
if a.type = vehicle and not InList(newloc, vehicle_path, a)
{
! "You can't pull..."
OMessage(attachable, 20, a)
return -1
}
#endif
if a and (a is not mobile and not Contains(a, att))
{
if a.attach_immobile
return -1
return att
}
if a.#attachable_to
{
b = Check_Attachable(a, newloc)
if b: return b
}
if a and child(a) and a ~= parent(att)
{
b = Check_AttachableChildren(a, newloc)
if b: return b
}
}
}
! Check_AttachableChildren checks to see what (if anything) in <obj> is
! an attachable.
routine Check_AttachableChildren(obj, newloc)
{
local b, i
for i in obj
{
if i.#attached_to
{
b = Check_Attachable(i, newloc)
if b: return b
}
elseif child(i)
{
b = Check_AttachableChildren(i, newloc)
if b: return b
}
}
}
! Check_Attachedto checks attachables in <obj>'s location. Returns the
! number of the first attachable keeping <obj> from moving; otherwise
! returns false.
routine Check_Attachedto(obj, oldloc, newloc)
{
local i, j, k
for i in oldloc
{
for (j=1; j<=i.#attached_to; j++)
{
k = i.attached_to #j
if k = obj or Contains(obj, k):
{
#ifset USE_VEHICLES
if k.type = vehicle and
not InList(newloc, vehicle_path, k):
{
! "You can't pull..."
OMessage(attachable, 20, k)
return -1
}
#endif
if k is not mobile and
k~=obj and not Contains(obj, k)
{
if k.attach_immobile
return -1
return i
}
}
}
if child(i)
{
k = Check_Attachedto(obj, child(i))
if k
return k
}
}
}
! MoveAllAttachables moves anything connected (no matter how tenuously)
! to <obj> from <oldloc> to <newloc>. Returns the number of the first
! object (even if there is more than one) being carried along. The <q>
! argument is optional; if true, the objects are moved quietly, i.e.
! without a message being printed.
routine MoveAllAttachables(obj, oldloc, newloc, q)
{
local first, second
obj is not workflag
! First, move all attachables in the specified object:
first = Move_Attachables(obj, oldloc, newloc)
! Then, move anything else in the old location that might somehow be
! attached to it:
second = Move_Attachedto(obj, oldloc, newloc)
if not first: first = second
if first and not q
! "With it (attached) to..."
OMessage(attachable, 21, first)
return first
}
! Move_Attachables moves all objects attached by an attachable item in <obj>.
routine Move_Attachables(obj, oldloc, newloc)
{
local i, k, first
for i in obj
{
k = Move_AttachedObjects(i, obj, oldloc, newloc)
if not first: first = k
}
return first
}
! Move_AttachedObjects is called to move everything attached to the
! attachable <att> in <oldloc> to <newloc>.
routine Move_AttachedObjects(att, obj, oldloc, newloc)
{
local i, j, first
if att.#attached_to
{
for (i=1; i<=att.#attached_to; i++)
{
j = att.attached_to #i
if j
{
if Contains(oldloc, j) and j not in obj
{
if obj is not workflag
{
first = att
obj is workflag
}
move j to newloc
j is moved
Move_Attachables(j, oldloc, newloc)
Move_Attachedto(j, oldloc, newloc)
}
}
}
}
return first
}
! Move_Attachedto scans <oldloc> for anything attached to <obj>, moving
! any valid matches to <newloc>. Not particularly graceful, since it must
! recurse inward for each object, since attachments are explicit only from
! attachable to object, and not vice-versa.
routine Move_Attachedto(obj, oldloc, newloc)
{
local i, j, k, first
for i in oldloc
{
for (j=1; j<=i.#attached_to; j++)
{
if i.attached_to #j = obj or
Contains(obj, i.attached_to #j):
{
move i to newloc
i is moved
if not first: first = i
k=Move_AttachedObjects(i, obj, oldloc, newloc)
if not first: first = k
k=Move_Attachables(i, obj, oldloc, newloc)
if not first: first = k
}
}
if child(i)
{
k = Move_Attachedto(obj, child(i), newloc)
if not first: first = k
}
}
return first
}
! ListAttachments prints everything attached to the attachable <obj>.
! Basically an adaptation of PropertyList from HUGOLIB.H.
routine ListAttachments(obj)
{
local a, b, n, total
for (a=1; a<=obj.#attached_to; a++)
{
if obj.attached_to #a: total++
}
for (a=1; a<=obj.#attached_to; a++)
{
b = obj.attached_to #a
if b
{
The(b)
if Contains(player, b)
OMessage(attachable, 22) ! " you're holding"
if a < total and total > 2
print ", ";
elseif a = total - 1: print " ";
if a = total - 1
print "and ";
n++
}
}
return n
}
#endif ! ifset USE_ATTACHABLES
!----------------------------------------------------------------------------
! OBJECT LIBRARY MESSAGES
!
! OMessage and NewOMessages mirror HUGOLIB.H's Message and NewMessages,
! respectively, although not exactly. The difference is that the first
! argument passed to OMessage (or NewOMessages) is an object or class,
! not a routine address.
!
! OMessage(object, number)
! prints message <number> for object/class <object>
!
! OMessage (object, number, var1, var2)
! where one or two variables--which may be objects or any other value--
! are used by message <number> for object/class <object>
routine OMessage(obj, num, a, b)
{
! Check first to see if the NewOMessages routine provides a
! replacement message:
!
if NewOMessages(obj, num, a, b): return
select obj
case door
{
select num
case 1
{
print CThe(player); " will have to get ";
if parent(player).prep #2
print parent(player).prep #2; " ";
else
print "out ";
print "of "; The(parent(player)); " first."
}
case 2: print "(opening "; The(self); " first)"
case 3: print CThe(self); IsorAre(self, true); " locked."
case 4: print CThe(self); IsorAre(self, true); " closed."
case 5
{
CThe(actor)
print " open"; MatchSubject(actor); " "; \
The(self); " and ";
if actor in location
print "leave"; MatchSubject(actor);
elseif location = self.between #((parent(actor) = \
self.between #1) + 1)
print "come"; MatchSubject(actor); " in";
print ", closing it behind "; actor.pronoun #2;
if actor is plural
print "selves."
else
print "self."
}
case 6: print CThe(self); IsorAre(self, true); " open."
case 7: print CThe(self); IsorAre(self, true); " closed."
}
case component: print CThe(player); " can't separate "; \
The(self); " from "; The(self.part_of); "."
#ifset USE_VEHICLES
case vehicle
{
select num
case 1
{
print "To walk, "; CThe(player); " will have to
get "; self.prep #2; " of "; The(self); \
". Otherwise, try \""; self.vehicle_verbs; \
"\" ";
if self.#vehicle_verbs > 1
print "or \""; self.vehicle_verbs #2; "\" ";
print "and a direction."
}
case 2: print CThe(player); IsorAre(player); \
" not in anything at the moment."
case 3: print CThe(player); IsorAre(player); " "; \
a.prep; " "; The(a);"--try \""; a.vehicle_verbs; "\"."
case 4: print "You'll have to specify a direction as well."
case 5: print CArt(obstacle); " stop"; \
MatchSubject(obstacle); " "; \
The(player, true); " from going anywhere."
case 6
{
print CThe(player); " can't "; a.vehicle_verbs; " ";
if object.prep
print object.prep;
else
print "in";
print " "; The(object); "."
}
case 7: print CThe(player); " can't "; a.vehicle_verbs; \
" that way."
}
#endif ! ifset USE_VEHICLES
#ifset USE_PLURAL_OBJECTS
case plural_class
{
select num
case 1
{
if pluralobj_heldmode = 1
print CThe(player); \
MatchPlural(player, "doesn't", "don't"); \
" have any ";
else
print "There are no ";
print self.noun; " to "; word[1]; "."
}
case 2
{
if pluralobj_heldmode = 1
print CThe(player); " only"; \
MatchPlural(player, "has", "have");
else
{
print "There ";
if a = 1: print "is";
else: print "are";
print " only ";
}
print NumberWord(a); " ";
if a > 1
print self.noun;
else
print self.single_noun;
print " to "; word[1]; "."
}
case 3: print CThe(player); " will have to do that one "; \
self.single_noun; " at a time."
}
#endif ! ifset USE_PLURAL_OBJECTS
#ifset USE_ATTACHABLES
case attachable
{
select num
case 1
{
print CArt(self); IsorAre(self, true); " "; \
self.attached_desc; " ";
ListAttachments(self)
print "."
}
case 2: print CThe(player); " see"; MatchSubject(player); \
" nothing special about "; The(self); "."
case 3
{
print CThe(self); IsorAre(self, true); " "; \
self.attached_desc; " ";
ListAttachments(self)
print "."
}
case 4: print CThe(player); " will have to "; \
self.detach_verb; " before you can take it."
case 5: print "(having to "; self.detach_verb; " "; \
The(self); " "; self.detach_prep; " "; \
The(self.attached_to #a); " first)"
case 6: print CThe(player); " can't "; word[1]; " "; \
The(object); "."
case 7: print "You might want to try \""; a; "\" with "; \
The(object); " instead of "; "\""; word[1]; "\"."
case 8:
{
print "You'll have to be a little more specific
about what you'd like ";
if player_person ~= 2: print The(player, true); " ";
print "to "; object.attach_verb; " "; \
The(object); " "; object.attach_prep; "."
}
case 9: print "(taking "; The(object); " first)"
case 10: print CThe(player); " can't "; word[1]; " "; \
The(object); " "; object.attach_prep; " "; \
The(xobject); "."
case 11: print "Except that "; The(object); \
IsorAre(object, true); " already "; \
object.attached_desc; " "; The(xobject); "."
case 12
{
print "Except that "; The(object); " is already "; \
object.attached_desc; " ";
ListAttachments(object)
print "."
}
case 13: print CThe(player); " "; object.attach_verb; \
MatchSubject(player); " "; The(object); " "; \
object.attach_prep; " "; The(xobject); "."
case 14: print "Except that "; The(object); \
IsorAre(object, true); " not "; \
object.attached_desc; " "; The(xobject); "."
case 15: print "Except that "; The(object); \
IsorAre(object, true); " not "; \
object.attached_desc; " anything."
case 16:
{
print "You'll have to be a little more specific
about what you'd like ";
if player_person ~= 2: print The(player, true); " ";
print "to "; word[1]; " "; The(object); " "; \
object.detach_prep; "."
}
case 17: print CThe(player); " "; object.detach_verb; \
MatchSubject(player); " "; The(object); " "; \
object.detach_prep; " "; The(xobject); "."
case 18
{
print CThe(player); " can't go anywhere while "; \
The(a); IsorAre(a, true); " still "; \
a.attached_desc; " ";
ListAttachments(a)
print "."
}
case 19
{
print CThe(player); " can't move whatever is "; \
a.attached_desc; " "; The(a); ", namely ";
ListAttachments(a)
print "."
}
case 20: print CThe(player); " can't pull "; The(a); \
" that way."
case 21
{
print "(with "; The(a); " "; a.attached_desc; " ";
ListAttachments(a)
print ")"
}
case 22: print " "; The(player); IsorAre(player); " holding";
}
#endif ! ifset USE_ATTACHABLES
}
routine NewOMessages(obj, num, a, b) ! The NewOMessages routine may be
{ ! REPLACED, and should return true
return false ! if a replacement message <num>
} ! exists for object/class <obj>
#endif ! ifset _COMPILING_HUGOLIB